home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / DMA_RTI.ZIP / dma_rti.txt
Text File  |  1995-03-25  |  19KB  |  463 lines

  1.   ┌──────────────────────[ Rage Technologies, Inc. ]─────────────────────────┐
  2.   │                                                                          │
  3.   │                             - Members -                                  │
  4.   │                       ] Myth: Ideas / Coder [                            │
  5.   │                      ] Night Stalker: Coder [                            │
  6.   │                       ] SKoRPiON: Musician [                             │
  7.   │                                                                          │
  8.   │                          - Support Board -                               │
  9.   │          ] Shadow Lands: (407) 851-2313, run by Night Stalker [          │
  10.   │                                                                          │
  11. ──┴──────────────────────────────────────────────────────────────────────────┴──
  12.                   How to program the DMA - by Night Stalker
  13. ────────────────────────────────────────────────────────────────────────────────
  14.     
  15.     What is the DMA?
  16.  
  17.     The DMA is another chip on your motherboard (usually is an Intel 8237
  18. chip) that allows you (the programmer) to offload data transfers between
  19. I/O boards.  DMA actually stands for 'Direct Memory Access'.
  20.  
  21.     An example of DMA usage would be the Sound Blaster's ability to play
  22. samples in the background.  The CPU sets up the sound card and the DMA.  When
  23. the DMA is told to 'go', it simply shovels the data from RAM to the card.
  24. Since this is done off-CPU, the CPU can do other things while the data is
  25. being transferred.
  26.  
  27.     Enough basics.  Here's how you program the DMA chip.
  28.  
  29. ────────────────────────────────────────────────────────────────────────────────
  30.  
  31.     When you want to start a DMA transfer, you need to know three things:
  32.  
  33.         - Where the memory is located (what page),
  34.         - The offset into the page, and
  35.         - How much you want to transfer.
  36.  
  37.     Since the DMA can work in both directions (memory to I/O card, and I/O
  38. card to memory), you can see how the Sound Blaster can record as well as
  39. play by using DMA.
  40.  
  41.     The DMA has two restrictions which you must abide by:
  42.  
  43.         - You cannot transfer more than 64K of data in one shot, and
  44.         - You cannot cross a page boundary.
  45.  
  46.     Restriction #1 is rather easy to get around.  Simply transfer the first
  47. block, and when the transfer is done, send the next block.
  48.  
  49.     For those of you not familiar with pages, I'll try to explain.
  50.  
  51.     Picture the first 1MB region of memory in your system.  It is divided
  52. into 16 pages of 64K a piece like so:
  53.  
  54.                     Page            Segment:Offset address
  55.                     ----            ----------------------
  56.                     0               0000:0000 - 0000:FFFF
  57.                     1               1000:0000 - 1000:FFFF
  58.                     2               2000:0000 - 2000:FFFF
  59.                     3               3000:0000 - 3000:FFFF
  60.                     4               4000:0000 - 4000:FFFF
  61.                     5               5000:0000 - 5000:FFFF
  62.                     6               6000:0000 - 6000:FFFF
  63.                     7               7000:0000 - 7000:FFFF
  64.                     8               8000:0000 - 8000:FFFF
  65.                     9               9000:0000 - 9000:FFFF
  66.                     A               A000:0000 - A000:FFFF
  67.                     B               B000:0000 - B000:FFFF
  68.                     C               C000:0000 - C000:FFFF
  69.                     D               D000:0000 - D000:FFFF
  70.                     E               E000:0000 - E000:FFFF
  71.                     F               F000:0000 - F000:FFFF
  72.  
  73.     This might look a bit overwhelming.  Not to worry if you're a C
  74. programmer, as I'm going to assume you know the C language for the examples
  75. in this text.  All the code in here will compile with Turbo C 2.0.
  76.  
  77.     Okay, remember the three things needed by the DMA?  Look back if you
  78. need to.  We can stuff this data into a structure for easy accessing:
  79.  
  80. typedef struct
  81. {
  82.     char page;
  83.     unsigned int offset;
  84.     unsigned int length;
  85. } DMA_block;
  86.  
  87.     Now, how do we find a memory pointer's page and offset?  Easy.  Use
  88. the following code:
  89.  
  90. void LoadPageAndOffset(DMA_block *blk, char *data)
  91. {
  92.     unsigned int temp, segment, offset;
  93.     unsigned long foo;
  94.  
  95.     segment = FP_SEG(data);
  96.     offset  = FP_OFF(data);
  97.  
  98.     blk->page = (segment & 0xF000) >> 12;
  99.     temp = (segment & 0x0FFF) << 4;
  100.     foo = offset + temp;
  101.     if (foo > 0xFFFF)
  102.         blk->page++;
  103.     blk->offset = (unsigned int)foo;
  104. }
  105.  
  106.     Most (if not all) of you are probably thinking, "What the heck is he doing
  107. there?"  I'll explain.
  108.  
  109.     The FP_SEG and FP_OFF macros find the segment and the offset of the data
  110. block in memory.  Since we only need the page (look back at the table above),
  111. we can take the upper 4 bits of the segment to create our page.
  112.  
  113.     The rest of the code takes the segment, adds the offset, and sees if the
  114. page needs to be advanced or not.  (Note that a memory region can be located at
  115. 2FFF:F000, and a single byte increase will cause the page to increase by one.)
  116.  
  117.     In plain English, the page is the highest 4 bits of the absolute 20 bit
  118. address of our memory location.  The offset is the lower 12 bits of the
  119. absolute 20 bit address plus our offset.
  120.  
  121.     Now that we know where our data is, we need to find the length.
  122.  
  123.     The DMA has a little quirk on length.  The true length sent to the DMA
  124. is actually length + 1.  So if you send a zero length to the DMA, it actually
  125. transfers one byte, whereas if you send 0xFFFF, it transfers 64K.  I guess
  126. they made it this way because it would be pretty senseless to program the
  127. DMA to do nothing (a length of zero), and in doing it this way, it allowed a
  128. full 64K span of data to be transferred.
  129.  
  130.     Now that you know what to send to the DMA, how do you actually start it?
  131. This enters us into the different DMA channels.
  132.  
  133. ────────────────────────────────────────────────────────────────────────────────
  134.  
  135.     The DMA has 4 different channels to send 8-bit data.  These channels are
  136. 0, 1, 2, and 3, respectively.  You can use any channel you want, but if you're
  137. transferring to an I/O card, you need to use the same channel as the card.
  138. (ie: Sound Blaster uses DMA channel 1 as a default.)
  139.  
  140.     There are 3 ports that are used to set the DMA channel:
  141.  
  142.         - The page register,
  143.         - The address (or offset) register, and
  144.         - The word count (or length) register.
  145.  
  146.     The following chart will describe each channel and it's corresponding
  147. port number:
  148.  
  149.                     DMA Channel   Page    Address  Count
  150.                     ────────────────────────────────────
  151.                          0         87h       0h      1h
  152.                          1         83h       2h      3h
  153.                          2         81h       4h      5h
  154.                          3         82h       6h      7h
  155.                          4         8Fh      C0h     C2h
  156.                          5         8Bh      C4h     C6h
  157.                          6         89h      C8h     CAh
  158.                          7         8Ah      CCh     CEh
  159.  
  160.       (Note: Channels 4-7 are 16-bit DMA channels.  See below for more info.)
  161.  
  162.  
  163.     Since you need to send a two-byte value to the DMA (the offset and the
  164. length are both two bytes), the DMA requests you send the low byte of data
  165. first, then the high byte.  I'll give a thorough example of how this is done
  166. momentarily.
  167.  
  168.     The DMA has 3 registers for controlling it's state.  Here is the bitmap
  169. layout of how they are accessed:
  170.  
  171. Mask Register (0Ah):
  172. ────────────────────
  173.  
  174.     MSB                             LSB
  175.       x   x   x   x     x   x   x   x
  176.       ─────────┬─────────   ┬   ──┬──
  177.                │            │     │     00 - Select channel 0 mask bit
  178.                │            │     └──── 01 - Select channel 1 mask bit
  179.                │            │           10 - Select channel 2 mask bit
  180.                │            │           11 - Select channel 3 mask bit
  181.                │            │
  182.                │            └──────────  0 - Clear mask bit
  183.                │                         1 - Set mask bit
  184.                │
  185.                └─────────────────────── xx - Don't care
  186.  
  187.  
  188. Mode Register (0Bh):
  189. ────────────────────
  190.  
  191.     MSB                             LSB
  192.       x   x   x   x     x   x   x   x
  193.       ──┬──   ┬   ┬     ──┬──   ──┬──
  194.         │     │   │       │       │     00 - Channel 0 select
  195.         │     │   │       │       └──── 01 - Channel 1 select
  196.         │     │   │       │             10 - Channel 2 select
  197.         │     │   │       │             11 - Channel 3 select
  198.         │     │   │       │
  199.         │     │   │       │             00 - Verify transfer
  200.         │     │   │       └──────────── 01 - Write transfer
  201.         │     │   │                     10 - Read transfer
  202.         │     │   │
  203.         │     │   └────────────────────  0 - Autoinitialized
  204.         │     │                          1 - Non-autoinitialized
  205.         │     │
  206.         │     └────────────────────────  0 - Address increment select
  207.         │
  208.         │                               00 - Demand mode
  209.         └────────────────────────────── 01 - Single mode
  210.                                         10 - Block mode
  211.                                         11 - Cascade mode
  212.  
  213.  
  214. DMA clear selected channel (0Ch):
  215. ─────────────────────────────────
  216.  
  217.     Outputting a zero to this port stops all DMA processes that are currently
  218. happening as selected by the mask register (0Ah).
  219.  
  220. ────────────────────────────────────────────────────────────────────────────────
  221.  
  222.     Some of the most common modes to program the mode register are:
  223.  
  224.             - 45h: Write transfer (I/O card to memory), and
  225.             - 49h: Read transfer (memory to I/O card).
  226.  
  227.     Both of these assume DMA channel 1 for all transfers.
  228.  
  229.     Now, there's also the 16-bit DMA channels as well.  These shove two bytes
  230. of data at a time.  That's how the Sound Blaster 16 works as well in 16-bit
  231. mode.
  232.  
  233.     Programming the DMA for 16-bits is just as easy as 8 bit transfers.  The
  234. only difference is you send data to different I/O ports.  The 16-bit DMA also
  235. uses 3 other control registers as well:
  236.  
  237. Mask Register (D4h):
  238. ────────────────────
  239.  
  240.     MSB                             LSB
  241.       x   x   x   x     x   x   x   x
  242.       ─────────┬─────────   ┬   ──┬──
  243.                │            │     │     00 - Select channel 4 mask bit
  244.                │            │     └──── 01 - Select channel 5 mask bit
  245.                │            │           10 - Select channel 6 mask bit
  246.                │            │           11 - Select channel 7 mask bit
  247.                │            │
  248.                │            └──────────  0 - Clear mask bit
  249.                │                         1 - Set mask bit
  250.                │
  251.                └─────────────────────── xx - Don't care
  252.  
  253.  
  254. Mode Register (D6h):
  255. ────────────────────
  256.  
  257.     MSB                             LSB
  258.       x   x   x   x     x   x   x   x
  259.       ──┬──   ┬   ┬     ──┬──   ──┬──
  260.         │     │   │       │       │     00 - Channel 4 select
  261.         │     │   │       │       └──── 01 - Channel 5 select
  262.         │     │   │       │             10 - Channel 6 select
  263.         │     │   │       │             11 - Channel 7 select
  264.         │     │   │       │
  265.         │     │   │       │             00 - Verify transfer
  266.         │     │   │       └──────────── 01 - Write transfer
  267.         │     │   │                     10 - Read transfer
  268.         │     │   │
  269.         │     │   └────────────────────  0 - Autoinitialized
  270.         │     │                          1 - Non-autoinitialized
  271.         │     │
  272.         │     └────────────────────────  0 - Address increment select
  273.         │
  274.         │                               00 - Demand mode
  275.         └────────────────────────────── 01 - Single mode
  276.                                         10 - Block mode
  277.                                         11 - Cascade mode
  278.  
  279.  
  280. DMA clear selected channel (D8h):
  281. ─────────────────────────────────
  282.  
  283.     Outputting a zero to this port stops all DMA processes that are currently
  284. happening as selected by the mask register (D4h).
  285.  
  286.  
  287.     Now that you know all of this, how do you actually use it?  Here is sample
  288. code to program the DMA using our DMA_block structure we defined before.
  289.  
  290. ────────────────────────────────────────────────────────────────────────────────
  291.  
  292. /* Just helps in making things look cleaner.  :) */
  293. typedef unsigned char   uchar;
  294. typedef unsigned int    uint;
  295.  
  296. /* Defines for accessing the upper and lower byte of an integer. */
  297. #define LOW_BYTE(x)         (x & 0x00FF)
  298. #define HI_BYTE(x)          ((x & 0xFF00) >> 8)
  299.  
  300. /* Quick-access registers and ports for each DMA channel. */
  301. uchar MaskReg[8]   = { 0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4 };
  302. uchar ModeReg[8]   = { 0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6 };
  303. uchar ClearReg[8]  = { 0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8 };
  304.  
  305. uchar PagePort[8]  = { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A };
  306. uchar AddrPort[8]  = { 0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC };
  307. uchar CountPort[8] = { 0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE };
  308.  
  309. void StartDMA(uchar DMA_channel, DMA_block *blk, uchar mode)
  310. {
  311.     /* First, make sure our 'mode' is using the DMA channel specified. */
  312.     mode |= DMA_channel;
  313.  
  314.     /* Don't let anyone else mess up what we're doing. */
  315.     disable();
  316.  
  317.     /* Set up the DMA channel so we can use it.  This tells the DMA */
  318.     /* that we're going to be using this channel.  (It's masked) */
  319.     outportb(MaskReg[DMA_channel], 0x04 | DMA_channel);
  320.  
  321.     /* Clear any data transfers that are currently executing. */
  322.     outportb(ClearReg[DMA_channel], 0x00);
  323.  
  324.     /* Send the specified mode to the DMA. */
  325.     outportb(ModeReg[DMA_channel], mode);
  326.  
  327.     /* Send the offset address.  The first byte is the low base offset, the */
  328.     /* second byte is the high offset. */
  329.     outportb(AddrPort[DMA_channel], LOW_BYTE(blk->offset));
  330.     outportb(AddrPort[DMA_channel], HI_BYTE(blk->offset));
  331.  
  332.     /* Send the physical page that the data lies on. */
  333.     outportb(PagePort[DMA_channel], blk->page);
  334.  
  335.     /* Send the length of the data.  Again, low byte first. */
  336.     outportb(CountPort[DMA_channel], LOW_BYTE(blk->length));
  337.     outportb(CountPort[DMA_channel], HI_BYTE(blk->length));
  338.  
  339.     /* Ok, we're done.  Enable the DMA channel (clear the mask). */
  340.     outportb(MaskReg[DMA_channel], DMA_channel);
  341.  
  342.     /* Re-enable interrupts before we leave. */
  343.     enable();
  344. }
  345.  
  346. void PauseDMA(uchar DMA_channel)
  347. {
  348.     /* All we have to do is mask the DMA channel's bit on. */
  349.     outportb(MaskReg[DMA_channel], 0x04 | DMA_channel);
  350. }
  351.  
  352. void UnpauseDMA(uchar DMA_channel)
  353. {
  354.     /* Simply clear the mask, and the DMA continues where it left off. */
  355.     outportb(MaskReg[DMA_channel], DMA_channel);
  356. }
  357.  
  358. void StopDMA(uchar DMA_channel)
  359. {
  360.     /* We need to set the mask bit for this channel, and then clear the */
  361.     /* selected channel.  Then we can clear the mask. */
  362.     outportb(MaskReg[DMA_channel], 0x04 | DMA_channel);
  363.  
  364.     /* Send the clear command. */
  365.     outportb(ClearReg[DMA_channel], 0x00);
  366.  
  367.     /* And clear the mask. */
  368.     outportb(MaskReg[DMA_channel], DMA_channel);
  369. }
  370.  
  371. uint DMAComplete(uchar DMA_channel)
  372. {
  373.     /* Register variables are compiled to use registers in C, not memory. */
  374.     register int z;
  375.  
  376.     z = CountPort[DMA_channel];
  377.     outportb(0x0C, 0xFF);
  378.  
  379.     /* This *MUST* be coded in Assembly!  I've tried my hardest to get it */
  380.     /* into C, and I've had no success.  :(  (Well, at least under Borland.) */
  381. redo:
  382.     asm {
  383.         mov  dx,z
  384.         in   al,dx
  385.         mov     bl,al
  386.         in     al,dx
  387.         mov  bh,al
  388.  
  389.         in     al,dx
  390.         mov     ah,al
  391.         in     al,dx
  392.         xchg ah,al
  393.  
  394.         sub  bx,ax
  395.         cmp  bx,40h
  396.         jg     redo
  397.         cmp     bx,0FFC0h
  398.         jl     redo
  399.     }
  400.     return _AX;
  401. }
  402.  
  403. ────────────────────────────────────────────────────────────────────────────────
  404.  
  405.     I think all the above functions are self explanatory except for the last
  406. one.  The last function returns the number of bytes that the DMA has
  407. transferred to (or read from) the device.  I really don't know how it works
  408. as it's not my code.  I found it laying on my drive, and I thought it might
  409. be somewhat useful to those of you out there.  You can find out when a DMA
  410. transfer is complete this way if the I/O card doesn't raise an interrupt.
  411. DMAComplete() will return -1 (or 0xFFFF) if there is no DMA in progress.
  412.  
  413.     Don't forget to load the length into your DMA_block structure as well
  414. before you call StartDMA().  (When I was writing these routines, I forgot
  415. to do that myself... I was wondering why it was transferring garbage.. <G>)
  416.  
  417.     I hope you all have caught on to how the DMA works by now.  Basically it
  418. keeps a list of DMA channels that are running or not.  If you need to change
  419. something in one of these channels, you mask the channel, and reprogram.  When
  420. you're done, you simply clear the mask, and the DMA starts up again.
  421.  
  422.     If anyone has problems getting this to work, I'll be happy to help.  Send
  423. us mail at the address below, and either I or another Rage member will fix
  424. your problem(s).
  425.  
  426.     Enjoy!
  427.                                                 - Night Stalker
  428.  
  429. -------------------------------------------------------------------------------
  430.  
  431. Look for other Rage Technologies, Inc. stuff coming soon:
  432.  
  433.         - Our first major demo, "Transvectoring".  The theme is to
  434.           show off our new 3-D engine with lightsourcing and texture
  435.           mapping... REALLY fast.  Also to show what objects beyond
  436.           3D really look like.  For example, a 4D or a 5D cube.  Maybe
  437.           more.  Expected release date:  Mid '95 (?)
  438.  
  439.         - Night Hawk 0.2α BBS.  The first BBS software to show that
  440.           ANSI is dead, and RIP is a thing of the past.  Features
  441.           include: True multitasking, full video and audio routines,
  442.           and more.  Expected release date:  Early/Mid '96.
  443.  
  444. -------------------------------------------------------------------------------
  445.  
  446. Other news:
  447.  
  448.         - Shadow Lands is *NOT* up!  Please don't call for a few weeks!
  449.           Night Stalker is in the process of rebuilding his system and
  450.           adding more disk space.  We are hoping that Night Stalker will
  451.           have Shadow Lands up by Mid-February.
  452.  
  453.         - Rage Technologies, Inc. has a mailing list.  If you'd like to
  454.           get ahold of any one of us, send E-mail to:
  455.  
  456.                                       ragetech@trappen.vsl.ist.ucf.edu
  457.  
  458.         - Rage Technologies, Inc. also has an experimental FTP server
  459.           running.  If you would like to get any Rage products, simply
  460.           anonymous FTP to:  trappen.vsl.ist.ucf.edu.  All Rage files
  461.           are located in /pub/ragetech.
  462.  
  463.